{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "bb0145c9c1f6ebcc",
   "metadata": {},
   "source": [
    "## Feast Client with RBAC\n",
    "\n",
    "## Feast Kubernetes RBAC Authorization\n",
    "\n",
    "Feast **Role-Based Access Control (RBAC)** in Kubernetes relies on a **service account** for authentication. This applies both **within a Kubernetes pod** and for **external clients** accessing Feast\n",
    "\n",
    "In this example, Feast will automatically retrieve the Kubernetes ServiceAccount token from pod path:\n",
    "```\n",
    "/var/run/secrets/kubernetes.io/serviceaccount/token\n",
    "```\n",
    "This means:\n",
    "- No manual configuration is needed inside a pod.\n",
    "- The token is mounted automatically and used for authentication.\n",
    "- Developer?User just need create the binding with role and service account accordingly.\n",
    "\n",
    "For more details, refer to the user guide: [Kubernetes RBAC Authorization](https://docs.feast.dev/master/getting-started/components/authz_manager#kubernetes-rbac-authorization). \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6590c081efb1fe3c",
   "metadata": {},
   "source": [
    "###  Feature Store settings\n",
    "**The Operator create client ConfigMap** containing the `feature_store.yaml `settings. We can retrieve it save it feature_repo folder."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "456fb4df46f32380",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:00:28.986653Z",
     "start_time": "2025-03-14T15:00:28.670157Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "project: feast_rbac\n",
      "provider: local\n",
      "offline_store:\n",
      "    host: feast-sample-kubernetes-auth-offline.feast.svc.cluster.local\n",
      "    type: remote\n",
      "    port: 443\n",
      "    scheme: https\n",
      "    cert: /tls/offline/tls.crt\n",
      "online_store:\n",
      "    path: https://feast-sample-kubernetes-auth-online.feast.svc.cluster.local:443\n",
      "    type: remote\n",
      "    cert: /tls/online/tls.crt\n",
      "registry:\n",
      "    path: feast-sample-kubernetes-auth-registry.feast.svc.cluster.local:443\n",
      "    registry_type: remote\n",
      "    cert: /tls/registry/tls.crt\n",
      "auth:\n",
      "    type: kubernetes\n",
      "entity_key_serialization_version: 3\n"
     ]
    }
   ],
   "source": [
    "!kubectl get configmap feast-sample-kubernetes-auth-client -n feast -o jsonpath='{.data.feature_store\\.yaml}' > client/feature_repo/feature_store.yaml\n",
    "!cat  client/feature_repo/feature_store.yaml"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e00a2f3",
   "metadata": {},
   "source": [
    "### Certificate Path Configuration\n",
    "\n",
    "The Feast operator automatically configures the correct certificate path based on the deployment environment:\n",
    "- For RHOAI/ODH deployments with custom CA bundle: Uses `/etc/pki/tls/custom-certs/service-ca.crt`\n",
    "- For standard deployments: Uses individual service certificate paths like `/tls/offline/tls.crt`\n",
    "\n",
    "No manual configuration is needed - the operator handles this automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fcb92e6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# The operator now automatically configures the correct certificate path\n",
    "# No manual modification needed - the feature_store.yaml already has the correct paths\n",
    "print(\"Certificate paths are automatically configured by the Feast operator\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84f73e09711bff9f",
   "metadata": {},
   "source": [
    "**Create ConfigMap From Feature Repository**  \n",
    "We need feature_repo inside the container. let's create configmap from `feature_repo` contains the feature repository files, including `feature-store.yaml` and `test.py`. It will be mounted as a volume in the deployment for the client examples to test the script."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "b840ac6ea3b95e90",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:02:03.243912Z",
     "start_time": "2025-03-14T15:02:02.804431Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "configmap/client-feature-repo-config created\n"
     ]
    }
   ],
   "source": [
    "!kubectl delete configmap client-feature-repo-config --ignore-not-found -n feast\n",
    "!kubectl create configmap client-feature-repo-config --from-file=client/feature_repo -n feast"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a70e78b9",
   "metadata": {},
   "source": [
    "## Testing"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b36bf167",
   "metadata": {},
   "source": [
    "### 0. Lets Run Materialization on the Feature Server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4bb72c04",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/opt/app-root/lib64/python3.11/site-packages/pydantic/_internal/_fields.py:192: UserWarning: Field name \"vector_enabled\" in \"SqliteOnlineStoreConfig\" shadows an attribute in parent \"VectorStoreConfig\"\n",
      "  warnings.warn(\n",
      "Materializing \u001b[1m\u001b[32m1\u001b[0m feature views from \u001b[1m\u001b[32m2025-05-08 12:00:00+00:00\u001b[0m to \u001b[1m\u001b[32m2025-05-20 12:00:00+00:00\u001b[0m into the \u001b[1m\u001b[32msqlite\u001b[0m online store.\n",
      "\n",
      "\u001b[1m\u001b[32mdriver_hourly_stats\u001b[0m:\n",
      "100%|████████████████████████████████████████████████████████████████| 5/5 [00:00<00:00, 770.93it/s]\n"
     ]
    }
   ],
   "source": [
    "!kubectl exec deploy/feast-sample-kubernetes-auth -itc online -- feast materialize -v driver_hourly_stats 2025-05-08T12:00:00Z 2025-05-20T12:00:00Z"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84e3f83699b8d83",
   "metadata": {},
   "source": [
    "### 1. Test Read-Only Feast User \n",
    "\n",
    "**Step 1: Deploy read-only user, we are using `serviceAccountName  feast-user-sa` in deployment.**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "14b7ad38368db767",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:03:23.308812Z",
     "start_time": "2025-03-14T15:03:22.868434Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "apiVersion: apps/v1\n",
      "kind: Deployment\n",
      "metadata:\n",
      "  name: client-readonly-user\n",
      "  namespace: feast\n",
      "  labels:\n",
      "    app: client-user\n",
      "spec:\n",
      "  replicas: 1\n",
      "  selector:\n",
      "    matchLabels:\n",
      "      app: client-user\n",
      "  template:\n",
      "    metadata:\n",
      "      labels:\n",
      "        app: client-user\n",
      "    spec:\n",
      "      serviceAccountName: feast-user-sa\n",
      "      containers:\n",
      "        - name: client-user-container\n",
      "          image: quay.io/feastdev/feature-server:latest\n",
      "          imagePullPolicy: Always\n",
      "          command: [\"sleep\", \"infinity\"]\n",
      "          volumeMounts:\n",
      "            - name: client-feature-repo-config\n",
      "              mountPath: /opt/app-root/src\n",
      "            - name: feast-service-ca\n",
      "              mountPath: /etc/pki/tls/custom-certs/service-ca.crt\n",
      "              subPath: service-ca.crt\n",
      "      volumes:\n",
      "        - name: client-feature-repo-config\n",
      "          configMap:\n",
      "            name: client-feature-repo-config\n",
      "        - name: feast-service-ca\n",
      "          configMap:\n",
      "            name: feast-sample-kubernetes-auth-client-cadeployment.apps/client-readonly-user created\n"
     ]
    }
   ],
   "source": [
    "# Create the deployment \n",
    "!cat client/readonly_user_deployment_tls.yaml\n",
    "!kubectl apply -f \"client/readonly_user_deployment_tls.yaml\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b2f60e1fd32c0a5",
   "metadata": {},
   "source": [
    "**Step 2: Run test.py script for client-readonly-user, readonly-user can only read or query all objects.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "c33f1966259a8a18",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:03:43.446664Z",
     "start_time": "2025-03-14T15:03:31.105949Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- List feature views ---\n",
      "Successfully listed 2 feature views:\n",
      "  - driver_hourly_stats_fresh\n",
      "  - driver_hourly_stats\n",
      "\n",
      "--- Fetching Historical Features for Training ---\n",
      "Successfully fetched training historical features:\n",
      "    driver_id  ... conv_rate_plus_val2\n",
      "0       1001  ...           10.201450\n",
      "1       1002  ...           20.377766\n",
      "2       1003  ...           30.407708\n",
      "\n",
      "[3 rows x 10 columns]\n",
      "\n",
      "--- Fetching Historical Features for Batch Scoring ---\n",
      "Successfully fetched batch scoring historical features:\n",
      "    driver_id  ... conv_rate_plus_val2\n",
      "0       1001  ...           10.529917\n",
      "1       1002  ...           20.251087\n",
      "2       1003  ...           30.462439\n",
      "\n",
      "[3 rows x 10 columns]\n",
      "\n",
      "--- Write to Feature Store ---\n",
      "\n",
      "*** PERMISSION DENIED *** User lacks permission to modify the feature store.\n",
      "\n",
      "--- Fetching Online Features ---\n",
      "Successfully fetched online features directly:\n",
      "\n",
      "acc_rate : [0.2032364010810852, 0.47476285696029663]\n",
      "conv_rate_plus_val1 : [1000.6724982261658, 1001.9941676259041]\n",
      "conv_rate_plus_val2 : [2000.6724982261658, 2002.994167625904]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Fetching Online Features via Feature Service ---\n",
      "Successfully fetched online features via feature service:\n",
      "\n",
      "conv_rate : [0.6724982261657715, 0.9941676259040833]\n",
      "conv_rate_plus_val1 : [1000.6724982261658, 1001.9941676259041]\n",
      "conv_rate_plus_val2 : [2000.6724982261658, 2002.994167625904]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Fetching Online Features via Push Source ---\n",
      "Successfully fetched online features via feature service:\n",
      "\n",
      "acc_rate : [None, None]\n",
      "avg_daily_trips : [None, None]\n",
      "conv_rate : [None, None]\n",
      "conv_rate_plus_val1 : [None, None]\n",
      "conv_rate_plus_val2 : [None, None]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Performing Push Source ---\n",
      "Unexpected error while pushing event: \n",
      "Exception ignored in: <function RemoteRegistry.__del__ at 0x7f4a03b8c360>\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 111, in __del__\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 108, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_interceptor.py\", line 782, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2250, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2231, in _close\n",
      "AttributeError: 'NoneType' object has no attribute 'StatusCode'\n"
     ]
    }
   ],
   "source": [
    "#Run test.py script from pod to test RBAC for client-readonly-user.\n",
    "# verify the logs for write operation will show below message \n",
    "# --- Write to Feature Store ---\n",
    "#*** PERMISSION DENIED *** User lacks permission to modify the feature store.\n",
    "\n",
    "!kubectl exec -n feast -it $(kubectl get pods -n feast -l app=client-user -o jsonpath=\"{.items[0].metadata.name}\") -- python test.py\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26a12a9b",
   "metadata": {},
   "source": [
    "**Step 3: Run API request for client-readonly-user, readonly-user can only read or query all objects.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c75b3ff",
   "metadata": {},
   "source": [
    "Required:\n",
    " - Clients CA Cert from `/etc/pki/tls/custom-certs/service-ca.crt`\n",
    " - Users Bearer Token\n",
    "    - Get the User token from the location `/var/run/secrets/kubernetes.io/serviceaccount/token` OR  `kubectl whoami -t` on the client pod.\n",
    "    - Replace the <client_user_token> below with token obtained from above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98037494",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"metadata\":{\"feature_names\":[\"driver_id\",\"conv_rate\",\"acc_rate\"]},\"results\":[{\"values\":[1001,1002],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"1970-01-01T00:00:00Z\",\"1970-01-01T00:00:00Z\"]},{\"values\":[0.5884456634521484,0.046199530363082886],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"2025-05-20T11:00:00Z\",\"2025-05-20T11:00:00Z\"]},{\"values\":[0.8458412289619446,0.7902957201004028],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"2025-05-20T11:00:00Z\",\"2025-05-20T11:00:00Z\"]}]}"
     ]
    }
   ],
   "source": [
    "# Run Curl command to test the RBAC for client-readonly-user.\n",
    "!kubectl exec -it $(kubectl get pods -n feast -l app=client-user -o jsonpath=\"{.items[0].metadata.name}\") -n feast -- curl --cacert /etc/pki/tls/custom-certs/service-ca.crt -X POST https://feast-sample-kubernetes-auth-online.feast.svc.cluster.local/get-online-features -H \"Content-Type: application/json\" -H \"Authorization: Bearer <client_user_token>\" -d '{\"features\": [\"driver_hourly_stats:conv_rate\",\"driver_hourly_stats:acc_rate\"], \"entities\":{\"driver_id\": [1001, 1002]}}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5e63a172da6d6d7",
   "metadata": {},
   "source": [
    "### 2. Test Unauthorized Feast User "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3844c66b",
   "metadata": {},
   "source": [
    "**Step 1: Run test.py script for client-unauthorized-user, unauthorized-user could not even view all objects.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "7fb94439606b4077",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:05:21.599673Z",
     "start_time": "2025-03-14T15:05:21.286300Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/client-unauthorized-user created\n"
     ]
    }
   ],
   "source": [
    "!kubectl apply -f \"client/unauthorized_user_deployment_tls.yaml\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "7aea5658325ab008",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:05:36.709759Z",
     "start_time": "2025-03-14T15:05:26.828228Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/opt/app-root/lib64/python3.11/site-packages/feast/feature_view.py:48: DeprecationWarning: Entity value_type will be mandatory in the next release. Please specify a value_type for entity '__dummy'.\n",
      "  DUMMY_ENTITY = Entity(\n",
      "\n",
      "--- List feature views ---\n",
      "No feature views found. You might not have access or they haven't been created.\n",
      "\n",
      "--- Fetching Historical Features for Training ---\n",
      "\n",
      "*** PERMISSION DENIED *** Cannot fetch historical features.\n",
      "\n",
      "--- Fetching Historical Features for Batch Scoring ---\n",
      "\n",
      "*** PERMISSION DENIED *** Cannot fetch historical features.\n",
      "\n",
      "--- Write to Feature Store ---\n",
      "\n",
      "*** PERMISSION DENIED *** User lacks permission to modify the feature store.\n",
      "\n",
      "--- Fetching Online Features ---\n",
      "\n",
      "*** PERMISSION DENIED *** Cannot fetch online features.\n",
      "\n",
      "--- Fetching Online Features via Feature Service ---\n",
      "\n",
      "*** PERMISSION DENIED *** Cannot fetch online features.\n",
      "\n",
      "--- Fetching Online Features via Push Source ---\n",
      "\n",
      "*** PERMISSION DENIED *** Cannot fetch online features.\n",
      "\n",
      "--- Performing Push Source ---\n",
      "Unexpected error while pushing event: Unable to find push source 'driver_stats_push_source'.\n",
      "Exception ignored in: <function RemoteRegistry.__del__ at 0x7f6ba19d0400>\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 111, in __del__\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 108, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_interceptor.py\", line 782, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2250, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2231, in _close\n",
      "AttributeError: 'NoneType' object has no attribute 'StatusCode'\n",
      "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n",
      "E0000 00:00:1748540859.045206       7 init.cc:232] grpc_wait_for_shutdown_with_timeout() timed out.\n"
     ]
    }
   ],
   "source": [
    "!kubectl exec -n feast -it $(kubectl get pods -n feast -l app=client-unauthorized-user -o jsonpath=\"{.items[0].metadata.name}\") -- python test.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e55144c",
   "metadata": {},
   "source": [
    "**Step 2: Run API request for Unauthorized User, Unauthorized user should not be able to even view the objects.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89a1f7e8",
   "metadata": {},
   "source": [
    "Required:\n",
    " - Users Bearer Token\n",
    "    - Get the User token from the location `/var/run/secrets/kubernetes.io/serviceaccount/token` OR  `kubectl whoami -t` on the client pod.\n",
    "    - Replace the <client_user_token> below with token obtained from above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "128d09af",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"{\\\"module\\\": \\\"feast.errors\\\", \\\"class\\\": \\\"FeastPermissionError\\\", \\\"message\\\": \\\"Permission error:\\\\nPermission feast_user_permission denied execution of ['READ_ONLINE'] to FeatureView:driver_hourly_stats: Requires roles ['feast-reader'],Permission feast_admin_permission denied execution of ['READ_ONLINE'] to FeatureView:driver_hourly_stats: Requires roles ['feast-writer']\\\"}\""
     ]
    }
   ],
   "source": [
    "# Run Curl command to test the RBAC for client-readonly-user.\n",
    "!kubectl exec -it $(kubectl get pods -n feast -l app=client-unauthorized-user -o jsonpath=\"{.items[0].metadata.name}\") -n feast -- curl --cacert /etc/pki/tls/custom-certs/service-ca.crt -X POST https://feast-sample-kubernetes-auth-online.feast.svc.cluster.local/get-online-features -H \"Content-Type: application/json\" -H \"Authorization: Bearer <client_user_token>\" -d '{\"features\": [\"driver_hourly_stats:conv_rate\",\"driver_hourly_stats:acc_rate\"], \"entities\":{\"driver_id\": [1001, 1002]}}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb78ced7c37ceb4c",
   "metadata": {},
   "source": [
    "## 3. Test Admin Feast User"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93e93b9c",
   "metadata": {},
   "source": [
    "**Step 1: Run test.py script for clientadmin, client-admin should be perform all operations on all objects.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2ee693d2436e282a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:05:48.131836Z",
     "start_time": "2025-03-14T15:05:47.921200Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "deployment.apps/client-admin-user created\n"
     ]
    }
   ],
   "source": [
    "!kubectl apply -f \"client/admin_user_deployment_tls.yaml\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "7a6133f052b9cfe1",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-03-14T15:06:04.283659Z",
     "start_time": "2025-03-14T15:05:51.977649Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- List feature views ---\n",
      "Successfully listed 2 feature views:\n",
      "  - driver_hourly_stats_fresh\n",
      "  - driver_hourly_stats\n",
      "\n",
      "--- Fetching Historical Features for Training ---\n",
      "Successfully fetched training historical features:\n",
      "    driver_id  ... conv_rate_plus_val2\n",
      "0       1001  ...           10.201450\n",
      "1       1002  ...           20.377766\n",
      "2       1003  ...           30.407708\n",
      "\n",
      "[3 rows x 10 columns]\n",
      "\n",
      "--- Fetching Historical Features for Batch Scoring ---\n",
      "Successfully fetched batch scoring historical features:\n",
      "    driver_id  ... conv_rate_plus_val2\n",
      "0       1001  ...           10.529917\n",
      "1       1002  ...           20.251087\n",
      "2       1003  ...           30.462439\n",
      "\n",
      "[3 rows x 10 columns]\n",
      "\n",
      "--- Write to Feature Store ---\n",
      "User has write access to the feature store.\n",
      "\n",
      "--- Fetching Online Features ---\n",
      "Successfully fetched online features directly:\n",
      "\n",
      "acc_rate : [0.2032364010810852, 0.47476285696029663]\n",
      "conv_rate_plus_val1 : [1000.6724982261658, 1001.9941676259041]\n",
      "conv_rate_plus_val2 : [2000.6724982261658, 2002.994167625904]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Fetching Online Features via Feature Service ---\n",
      "Successfully fetched online features via feature service:\n",
      "\n",
      "conv_rate : [0.6724982261657715, 0.9941676259040833]\n",
      "conv_rate_plus_val1 : [1000.6724982261658, 1001.9941676259041]\n",
      "conv_rate_plus_val2 : [2000.6724982261658, 2002.994167625904]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Fetching Online Features via Push Source ---\n",
      "Successfully fetched online features via feature service:\n",
      "\n",
      "acc_rate : [None, None]\n",
      "avg_daily_trips : [None, None]\n",
      "conv_rate : [None, None]\n",
      "conv_rate_plus_val1 : [None, None]\n",
      "conv_rate_plus_val2 : [None, None]\n",
      "driver_id : [1001, 1002]\n",
      "\n",
      "--- Performing Push Source ---\n",
      "Unexpected error while pushing event: \n",
      "Exception ignored in: <function RemoteRegistry.__del__ at 0x7f621abdc360>\n",
      "Traceback (most recent call last):\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 111, in __del__\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/feast/infra/registry/remote.py\", line 108, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_interceptor.py\", line 782, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2250, in close\n",
      "  File \"/opt/app-root/lib64/python3.11/site-packages/grpc/_channel.py\", line 2231, in _close\n",
      "AttributeError: 'NoneType' object has no attribute 'StatusCode'\n"
     ]
    }
   ],
   "source": [
    "!kubectl exec -n feast -it $(kubectl get pods -n feast -l app=client-admin -o jsonpath=\"{.items[0].metadata.name}\") -- python test.py\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d389914f",
   "metadata": {},
   "source": [
    "**Step 2: Run API request for admin-user, admin-user should be able to read features.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "334171de",
   "metadata": {},
   "source": [
    "Required:\n",
    " - Clients CA Cert from `/etc/pki/tls/custom-certs/service-ca.crt`\n",
    " - Users Bearer Token\n",
    "    - Get the User token from the location `/var/run/secrets/kubernetes.io/serviceaccount/token` OR  `kubectl whoami -t` on the client pod.\n",
    "    - Replace the <client_user_token> below with token obtained from above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "bfde6a7c",
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"metadata\":{\"feature_names\":[\"driver_id\",\"conv_rate\",\"acc_rate\",\"conv_rate_plus_val1\"]},\"results\":[{\"values\":[1001,1002],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"1970-01-01T00:00:00Z\",\"1970-01-01T00:00:00Z\"]},{\"values\":[0.5884456634521484,0.046199530363082886],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"2025-05-20T11:00:00Z\",\"2025-05-20T11:00:00Z\"]},{\"values\":[0.8458412289619446,0.7902957201004028],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"2025-05-20T11:00:00Z\",\"2025-05-20T11:00:00Z\"]},{\"values\":[1000.5884456634521,1001.0461995303631],\"statuses\":[\"PRESENT\",\"PRESENT\"],\"event_timestamps\":[\"1970-01-01T00:00:00Z\",\"1970-01-01T00:00:00Z\"]}]}"
     ]
    }
   ],
   "source": [
    "# Run Curl command to test the RBAC for client-readonly-user.\n",
    "!kubectl exec -it $(kubectl get pods -n feast -l app=client-admin -o jsonpath=\"{.items[0].metadata.name}\") -n feast -- curl --cacert /etc/pki/tls/custom-certs/service-ca.crt -X POST https://feast-sample-kubernetes-auth-online.feast.svc.cluster.local/get-online-features -H \"Content-Type: application/json\" -H \"Authorization: Bearer <client_user_token>\" -d '{\"features\": [\"driver_hourly_stats:conv_rate\",\"driver_hourly_stats:acc_rate\", \"transformed_conv_rate:conv_rate_plus_val1\"], \"entities\":{\"driver_id\": [1001, 1002], \"val_to_add\": [1000, 1001], \"val_to_add_2\": [2000, 2002]}}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38c54e92643e0bda",
   "metadata": {},
   "source": [
    "[Next: Uninstall](./3-uninstall.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
